#ifndef _L_TRI_MESH_111227_H
#define _L_TRI_MESH_111227_H

#include <vector>

#define BOOST_FORCE_SYMMETRIC_OPERATORS
#include <boost/operators.hpp>
#include <boost/assert.hpp>

#include <lmacros.h>
#include <lvectors.h>
#include <ltriangle.h>

LLIB_BEGIN
namespace geom {

class MeshVertex;
class MeshEdge;
class MeshFace;

class TriMesh {
public:
    /* Constructor : with no data set */
    TriMesh();
    /* Constructor : input vertex list and face vertex index list */
    TriMesh(std::vector<Vec3f>& vertexList, std::vector<Vec3i>& faceVertexIndexList);
    /* Constructor : copy construction */
    TriMesh(const TriMesh& m);
    /* Destructor : delete all lists */
    ~TriMesh();

    /* Assignment operator */
    TriMesh& operator=(const TriMesh& m);

    /* Set model deta */
    void            SetModelData(std::vector<Vec3f>& vertexList, std::vector<Vec3i>& faceVertexIndexList);

    /* Vertex query */
    MeshVertex      GetVertex(Int32 index) const;
    inline Int32    GetVertexNum() const { return _vertexNum; }

    /* Edge query */
    MeshEdge        GetEdge(Int32 index) const;
    inline Int32    GetEdgeNum() const { return _edgeNum; }

    /* Face query */
    MeshFace        GetFace(Int32 index) const;
    inline Int32    GetFaceNum() const { return _faceNum; }

    /* Internal vertex & index array query */
    inline const Float* GetVertexList() const { return (const Float*)_vertexList; }
    inline const Int32* GetFaceVertexIndexList() const { return (const Int32*)_faceVertexIndexList; }

private:
    /* Vertex list */
    Vec3f*  _vertexList;                /* input */
    Int32   _vertexNum;
    /* Face vertex/edge index list */
    Vec3i*  _faceVertexIndexList;       /* input */
    Vec3i*  _faceEdgeIndexList;
    Int32   _faceNum;
    /* Edge vertex/face index list */
    Vec2i*  _edgeVertexIndexList;
    Vec2i*  _edgeFaceIndexList;
    Int32   _edgeNum;
    /* Model build */
    Boolean _modelBuild;

    /* Invalid vertex/edge/face index */
    const static Int32 _invalidIndex = -1;

    /* Private functions */
    void resetModelData(std::vector<Vec3f>& vertexList, std::vector<Vec3i>& faceVertexIndexList);
    void modelCopy(const TriMesh& m);

    /* The friend class: MeshVertex, MeshEdge, MeshFace */
    friend MeshVertex;
    friend MeshEdge;
    friend MeshFace;
};

class MeshVertex : public boost::totally_ordered<MeshVertex> {
public:
    /* The default constructor */
    MeshVertex() : _index(_invalidVertexIndex), _mesh(NULL) {}

    /* Get the vertex index */
    inline Int32 GetIndex() const { BOOST_ASSERT(Valid()); return _index; }

    /* Get the original vertex reference */
    inline Vec3f& GetRef() const { BOOST_ASSERT(Valid()); return _mesh->_vertexList[_index]; };

    /* Check validation */
    inline Boolean Valid() const { return _mesh != NULL && _index != _invalidVertexIndex; }

    /* for totallly ordered operators */
    friend inline Boolean operator == (const MeshVertex& f1, const MeshVertex& f2) { return f1._mesh == f2._mesh && f1._index == f2._index; }
    friend inline Boolean operator <  (const MeshVertex& f1, const MeshVertex& f2) { return f1._mesh != f2._mesh ? f1._mesh < f2._mesh : f1._index < f2._index; }

    /* Get the triangle mesh which belongs to */
    inline TriMesh* GetTriMesh() const { return _mesh; }

private:
    /* The constructor with data */
    MeshVertex(TriMesh* mesh, Int32 index) : _index(index), _mesh(mesh) {}

    /* The TriMesh pointer */
    TriMesh*    _mesh;

    /* Vertex index */
    Int32       _index;

    /* Invalid index */
    const static Int32 _invalidVertexIndex = -1;

    /* Only the friend class has the access to construct a valid vertex */
    friend TriMesh;
    friend MeshEdge;
    friend MeshFace;
};

class MeshEdge : public boost::totally_ordered<MeshEdge> {
public:
    /* The default constructor */
    MeshEdge() : _index(_invalidEdgeIndex), _mesh(NULL) {}

    /* Get the edge index */
    inline Int32 GetIndex() const { BOOST_ASSERT(Valid()); return _index; }

    /* Get the vertex of edge */
    enum VertexName { VertexBegin = 0, VertexEnd = 1 };
    MeshVertex GetVertex(const VertexName name) const;
    MeshVertex GetVertex(const Int32 name) const;
    inline Vec3f& GetVertexRef(const VertexName name) const { return GetVertex(name).GetRef(); }
    inline Vec3f& GetVertexRef(const Int32 name) const { return GetVertex(name).GetRef(); }

    /* Get the face of edge */
    enum FaceName { FaceLeft = 0, FaceRight = 1 };
    MeshFace GetFace(const FaceName name) const;
    MeshFace GetFace(const Int32 name) const;

    /* Check validation */
    inline Boolean Valid() const { return _mesh != NULL && _index != _invalidEdgeIndex; }

    /* for totallly ordered operators */
    friend inline Boolean operator == (const MeshEdge& f1, const MeshEdge& f2) { return f1._mesh == f2._mesh && f1._index == f2._index; }
    friend inline Boolean operator <  (const MeshEdge& f1, const MeshEdge& f2) { return f1._mesh != f2._mesh ? f1._mesh < f2._mesh : f1._index < f2._index; }

    /* Get the triangle mesh which belongs to */
    inline TriMesh* GetTriMesh() const { return _mesh; }

private:
    /* The constructor with data */
    MeshEdge(TriMesh* mesh, Int32 index) : _index(index), _mesh(mesh) {}

    /* The TriMesh pointer */
    TriMesh*    _mesh;

    /* Edge index */
    Int32       _index;

    /* Invalid index */
    const static Int32 _invalidEdgeIndex = -1;

    /* Only the friend class has the access to construct a valid edge */
    friend TriMesh;
    friend MeshFace;
};

class MeshFace : public boost::totally_ordered<MeshFace> {
public:
    /* The default constructor */
    MeshFace() : _index(_invalidFaceIndex), _mesh(NULL) {}

    /* Get the face index */
    inline Int32 GetIndex() const { BOOST_ASSERT(Valid()); return _index; }

    /* Get the vertex of face */
    enum VertexName { VertexA = 0, VertexB = 1, VertexC = 2 };
    MeshVertex GetVertex(const VertexName name) const;
    MeshVertex GetVertex(const Int32 name) const;
    inline Vec3f& GetVertexRef(const VertexName name) const { return GetVertex(name).GetRef(); }
    inline Vec3f& GetVertexRef(const Int32 name) const { return GetVertex(name).GetRef(); }

    /* Get the edge of face */
    enum EdgeName { EdgeAB = 0, EdgeBC = 1, EdgeCA = 2 };
    MeshEdge GetEdge(const EdgeName name) const;
    MeshEdge GetEdge(const Int32 name) const;

    /* Check validation */
    inline Boolean Valid() const { return _mesh != NULL && _index != _invalidFaceIndex; }

    /* Get corresponding Tri3f type value */
    Tri3f GetTriangle() const;

    /* Get mesh face normal vector */
    Vec3f GetNormal() const;

    /* Neighbour check */
    inline Boolean IsNeighbour(const MeshFace& another) const { return CheckNeighbour(*this,another); }
    static Boolean CheckNeighbour(const MeshFace& first, const MeshFace& second);

    /* for totallly ordered operators */
    friend inline Boolean operator == (const MeshFace& f1, const MeshFace& f2) { return f1._mesh == f2._mesh && f1._index == f2._index; }
    friend inline Boolean operator <  (const MeshFace& f1, const MeshFace& f2) { return f1._mesh != f2._mesh ? f1._mesh < f2._mesh : f1._index < f2._index; }

    /* Get the triangle mesh which belongs to */
    inline TriMesh* GetTriMesh() const { return _mesh; }

private:
    /* The constructor with data */
    MeshFace(TriMesh* mesh, Int32 index) : _index(index), _mesh(mesh) {}

    /* The TriMesh pointer */
    TriMesh*    _mesh;

    /* Face index */
    Int32       _index;

    /* Invalid index */
    const static Int32 _invalidFaceIndex = -1;

    /* Only the friend class has the access to construct a valid edge */
    friend TriMesh;
    friend MeshEdge;
};

} /* namespace geom */
LLIB_END

#endif
